#include <BLEDEV.h> // 包含BLE设备的库，提供BLE功能的实现

// 广播数据和响应数据的缓冲区
uint8_t adv_data[31]; // 广播数据缓冲区
uint8_t adv_rsp_data[31]; // 扫描响应数据缓冲区

// 定义服务和特征的UUID
#define SERVICE_UUID1         "ff33" // 服务UUID1
#define SERVICE_UUID2         "4fafc2011fb5459e8fccc5c9c331914b" // 服务UUID2

#define CHARACTERISTIC_UUID1 "FF11" // 特征UUID1
#define CHARACTERISTIC_UUID2 "FF22" // 特征UUID2
#define CHARACTERISTIC_UUID3 "ff11c2011fb5459e8fccc5c9c33191ff" // 特征UUID3

// 设备连接状态变量
bool deviceConnected = false; // 当前设备连接状态
bool oldDeviceConnected = false; // 上一个设备连接状态

// BLE特征指针
BLECharacteristic* pCharacteristic1; // 特征指针1
BLECharacteristic* pCharacteristic2; // 特征指针2
BLECharacteristic* pCharacteristic3; // 特征指针3
BLEAdvertising *pAdvertising; // 广播指针

// 一个简单的用于通知的变量,用于特征的值
uint32_t value = 0;

// 自定义服务器回调类，继承 BLEServerCallbacks
class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect() { // 当设备连接时调用
        deviceConnected = true; // 更新连接状态
        Serial.println("onConnect..."); // 打印连接信息
    }

    void onDisconnect() { // 当设备断开连接时调用
        deviceConnected = false; // 更新连接状态
        Serial.println("onDisconnect..."); // 打印断开连接信息
    }


void onwrite(uint16_t prf_id, uint16_t att_idx, uint8_t* data, uint16_t data_len) {
    // 打印出写入操作的 profile ID、attribute index 和数据长度
    Serial.printf("prf_id:%d, att_idx:%d, data_len:%d\r\n", prf_id, att_idx, data_len);

    // 动态分配内存以存储接收到的字符串数据
    char* receivedData = (char*) malloc(data_len + 1);
    if (receivedData == NULL) {
        Serial.println("Memory allocation failed!");
        return;
    }

    // 将数据拷贝到字符串数组中
    memcpy(receivedData, data, data_len);
    receivedData[data_len] = '\0';  // 添加字符串结束符

    // 打印接收到的字符串
    Serial.printf("Received data: %s\r\n", receivedData);

    // 释放内存
    free(receivedData);
}
// void onwrite(uint16_t prf_id, uint16_t att_idx, uint8_t* data, uint16_t data_len) {
//     // 打印出写入操作的 profile ID、attribute index 和数据长度
//     Serial.printf("prf_id:%d, att_idx:%d, data_len:%d\r\n", prf_id, att_idx, data_len);

//     // 使用 new 动态分配内存
//     char* receivedData = new char[data_len + 1];

//     // 将数据拷贝到字符串数组中
//     memcpy(receivedData, data, data_len);
//     receivedData[data_len] = '\0';  // 添加字符串结束符

//     // 打印接收到的字符串
//     Serial.printf("Received data: %s\r\n", receivedData);

//     // 释放内存
//     delete[] receivedData;
// }


    // 当客户端从特征读取数据时触发
    void onread(uint16_t prf_id,uint16_t att_idx,uint8_t* data,uint16_t *data_len) {
      Serial.printf("prf_id:%d,att_idx:%d\r\n",prf_id,att_idx); // 打印读取请求的信息
      // 向客户端返回固定的数据
      data[0]=0xff;// 设置返回的数据
      data[1]=0xee;
      data[2]=0xdd;
      *data_len = 3;  // 设置返回数据的长度,数据长度为3字节
    }
};

void setup() {
  // 初始化串口通讯
  Serial.begin(115200);
  
  // 初始化 BLE 堆栈
  BLEDEV::init();
  
  // 创建一个 BLE 服务器
  BLEServer* pServer = BLEDEV::createServer();
  
  // 获取BLE广播对象
  pAdvertising = BLEDEV::getAdvertising();
  
  // 创建第一个服务
  BLEService *pService1 = pServer->createService(SERVICE_UUID1);
  
  // 创建第二个服务
  BLEService *pService2 = pServer->createService(SERVICE_UUID2);
  
  // 为服务器设置自定义的回调
  pServer->setCallbacks(new MyServerCallbacks());

  // 为第一个服务创建特征1，支持读、写、通知
    pCharacteristic1 = pService1->createCharacteristic(
        CHARACTERISTIC_UUID1,
        BLECharacteristic::PROPERTY_READ   | // 允许读取
        BLECharacteristic::PROPERTY_WRITE  | // 允许写入
        BLECharacteristic::PROPERTY_NOTIFY  // 允许通知
    );

  // 为第一个服务创建特征2，支持写和指示
    pCharacteristic2 = pService1->createCharacteristic(
        CHARACTERISTIC_UUID2,
        BLECharacteristic::PROPERTY_WRITE | // 允许写入
        BLECharacteristic::PROPERTY_INDICATE // 允许指示
    );

  // 为第二个服务创建特征3，支持读写
    pCharacteristic3 = pService2->createCharacteristic(
        CHARACTERISTIC_UUID3,
        BLECharacteristic::PROPERTY_WRITE | // 允许写入
        BLECharacteristic::PROPERTY_READ // 允许读取
    );                                                           
  // 启动两个服务
  pService1->start();
  pService2->start();

  // 设置广告数据
	adv_data[0] = 0x0A;  // 广告数据长度
	adv_data[1] = 0x09;  // 设备名称类型
	memcpy(&adv_data[2], "7238_BLE", 9);  // 设备名称为“7238_BLE”
  pAdvertising->setAdvertisementData((uint8_t*)&adv_data, 0xb);  // 设置广告数据

  // 设置扫描响应数据
	adv_rsp_data[0] = 0x06;  // 扫描响应数据长度
	adv_rsp_data[1] = 0x08;  // 设备名称类型
	memcpy(&adv_rsp_data[2], "7238", 5);  // 扫描响应名称为“7238”
  pAdvertising->setScanResponseData((uint8_t*)&adv_rsp_data, 7);  // 设置扫描响应数据

  // 开始广播
  pAdvertising->start();
}

void loop() {
    // 主代码，循环运行：
    if (deviceConnected) { // 如果设备已连接
    delay(500);
    
    // 更新特征1的值并发送通知
    pCharacteristic1->setValue((uint8_t*)&value, 4);// 设置特征1的值
    pCharacteristic1->notify();// 通知特征1的值变化
    value++;  // 增加值
    delay(500);
    
    // 更新特征2的值并发送指示
    pCharacteristic2->setValue((uint8_t*)&value, 4);// 设置特征2的值
    pCharacteristic2->indicate();// 通知特征2的值变化

    delay(500);
  }

  // 处理断开连接后的广告重启
  if (!deviceConnected && oldDeviceConnected) {// 如果设备已断开而之前已连接
    delay(500); // 给蓝牙堆栈一些时间准备
    pAdvertising->start();  // 重新启动广播
    Serial.println("start advertising");// 打印开始广播的信息
    oldDeviceConnected = deviceConnected;// 更新旧连接状态
  }

  // 处理新连接的设备
  if (deviceConnected && !oldDeviceConnected) {// 如果设备已连接而之前未连接
    oldDeviceConnected = deviceConnected;// 更新旧连接状态
  }
}
